package com.tree.thread.pubsub;

import java.util.LinkedList;
import java.util.Random;

/**
 * @ClassName com.tree.thread.pubsub
 * Description: <类功能描述>. <br>
 * <p>
 * <使用说明>
 * </p>
 * @Author tree
 * @Date 2019-11-01 23:33
 * @Version 1.0
 */
public class WaitNotifyTest {

    public static void main(String[] args) {
        WaitNotifyStorage storage = new WaitNotifyStorage();

        for(int i = 0; i < 5; i++){
            new Thread(new Producer(storage), "生产者-"+i).start();
            new Thread(new Consumer(storage), "消费者-"+i).start();
        }
    }


    static class WaitNotifyStorage implements IStorage{
        /**
         * 存储最大值
         */
        private final int MAX_SIZE = 10;

        private final static String LOCK = "lock";

        /**
         * 值
         */
        private int count;

        /**
         * 存储载体
         */
        private volatile LinkedList<Integer> list = new LinkedList();


        @Override
        public void produce (){
            synchronized (LOCK){
                while (list.size() >= MAX_SIZE){
                    try {
                        System.out.println("链表已经达到最大值， 无法再生产，请等待");
                        LOCK.wait();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
                count++;
                list.push(count);
                System.out.println("生产者 "+Thread.currentThread().getName()+" 总共有 " + count+" 个资源");
                //可以看出我们使用synchronized可以实现生产者/消费者模型，但是我们得要注意一点，我们在这里使用的 notifyAll() 这个方法，为什么不能用 notify() ，也就是随便叫醒一个消费者呢？答案是不可以，使用 notify() 是叫醒 LOCK 阻塞队列里面的任意一个线程，假如此时我们的临界区域已经满了，此时唤醒的是一个生产者线程，就会导致死锁，所以我们在这里采用的是 notifyAll() 这个方法，意思就是唤醒阻塞队列里面的全部线程，这样某一个消费者就可以去取出临界区里面的产品，从而避免死锁的发生，但是很显然，从上面打印的结果可以看出，顺序是无法保证的，想要保证顺序，可以试着使用可重入锁 ReentrantLock 来实现。
                LOCK.notifyAll(); //
            }
        }

        @Override
        public void consume(){
            synchronized (LOCK){
                //为什么这里不能用if,用if会出现list为负数，导致链表抛异常?
                // 因为当减到0时， notify all， 如果当前线程抢到， 那么就会继续走下去，导致出现负数的情况
                while (list.size() <= 0){
                    try {
                        System.out.println("链表【"+Thread.currentThread().getName()+"】已经达到最大值， 无法再消费，请等待");
                        LOCK.wait();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }

                count --;
                System.out.println("消费者 "+Thread.currentThread().getName()+" 总共有 " + count+" 个资源， list.size="+list.size());
                list.pop();

                //可以看出我们使用synchronized可以实现生产者/消费者模型，但是我们得要注意一点，我们在这里使用的 notifyAll() 这个方法，为什么不能用 notify() ，也就是随便叫醒一个消费者呢？答案是不可以，使用 notify() 是叫醒 LOCK 阻塞队列里面的任意一个线程，假如此时我们的临界区域已经满了，此时唤醒的是一个生产者线程，就会导致死锁，所以我们在这里采用的是 notifyAll() 这个方法，意思就是唤醒阻塞队列里面的全部线程，这样某一个消费者就可以去取出临界区里面的产品，从而避免死锁的发生，但是很显然，从上面打印的结果可以看出，顺序是无法保证的，想要保证顺序，可以试着使用可重入锁 ReentrantLock 来实现。
                LOCK.notifyAll(); //


            }
        }

    }




}


